package com.peaksport.framework.uuid;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.SecureRandom;

import com.peaksport.framework.exception.PKDevException;

/**
 * Created by jinbin_guo on 2018-04-19.
 */
public class PKUuid implements Serializable {
    private static final long serialVersionUID = -65533L;
    private transient Uuid uuid;
    private transient PKEntityType type;
    private transient byte encoding;
    public static synchronized PKUuid create(PKEntityType type) {
        assert (type != null);
        try {
            return new PKUuid(Uuid.create(), type, (byte)1);
        } catch (Exception exc) {
            exc.printStackTrace();
            return null;
        }
    }

    public static PKUuid create(String typeString) {
        if (typeString == null)
            throw new RuntimeException("Invalid typeString.");
        assert ((typeString.length() == 8) || (typeString.length() == 4));

        return create(PKEntityType.create(typeString));
    }

    private PKUuid(Uuid uuid, PKEntityType type, byte encoding) {
        if (type == null) {
            throw new RuntimeException("Invalid ECAObjectType.");
        }

        if (type.toInteger() == 0)
            this.encoding = 0;
        else {
            this.encoding = encoding;
        }
        this.uuid = uuid;
        this.type = type;
    }

    public PKEntityType getType() {
        return this.type;
    }

    public static PKUuid read(String id) throws Exception {
        if ((id == null) || (id.length() == 0)) {
            throw new IllegalArgumentException("id is null or length is 0. '" + id + "'");
        }

        if ((id.length() == 44) || (id.length() == 40)) {
            Uuid uuid = Uuid.read(id.substring(0, 36));
            String type = id.substring(36).intern();
            return new PKUuid(uuid, PKEntityType.create(type), (byte)0);
        }
        if (id.length() == 28) {
            if (id.charAt(id.length() - 1) != '=') {
                throw new IllegalArgumentException( "the argument 'id' is invalid. '" + id + "'");
            }

            byte[] array = Base64Encoder.base64ToByteArray(id);
            DataInput in = new DataInputStream(new ByteArrayInputStream(array));
            try {
                Uuid uuid = Uuid.read(in);
                PKEntityType type = PKEntityType.read(in);
                return new PKUuid(uuid, type, (byte)1);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }
        if (id.length() == 16) {
            byte[] array = Base64Encoder.base64ToByteArray(id);
            DataInput in = new DataInputStream(new ByteArrayInputStream(array));
            try {
                Uuid uuid = Uuid.read2(in);
                PKEntityType type = PKEntityType.read(in);
                return new PKUuid(uuid, type, (byte)2);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }

        }

        throw new IllegalArgumentException("The argument 'id' is invalid. '"  + id + "' length:" + id.length());
    }

    public String toString() {
        if (this.encoding == 1) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(28);
            DataOutput out = new DataOutputStream(baos);
            try {
                this.uuid.write(out);
                this.type.write(out);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
            return Base64Encoder.byteArrayToBase64(baos.toByteArray());
        }
        if (this.encoding == 2) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(16);
            DataOutput out = new DataOutputStream(baos);
            try {
                this.uuid.write2(out);
                this.type.write(out);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
            return Base64Encoder.byteArrayToBase64(baos.toByteArray());
        }
        if (this.encoding == 0) {
            return this.uuid.toString() + this.type.toString();
        }
        throw new AssertionError("unhandled encoding.");
    }

    public static boolean isValidLength(String uuid) {
        if ((uuid == null) || (uuid.length() == 0)) {
            return false;
        }

        return ((uuid.length() == 44) || (uuid.length() == 40)
                || (uuid.length() == 28) || (uuid.length() == 16));
    }

    public static boolean isValidLength(String uuid, boolean trim) {
        if ((uuid == null) || (uuid.length() == 0)) {
            return false;
        }
        String id = null;
        if (trim)
            id = uuid.trim();
        else {
            id = uuid;
        }
        return isValidLength(id);
    }

    public static boolean isValid(String uuid, boolean trim) {
        if (!(isValidLength(uuid, trim)))
            return false;
        try {
            read(uuid.trim());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public static PKEntityType getECAObjectType(String uuid) {
        if ((!(isValid(uuid, true)))) {
            throw PKDevException.throwException("不合法的ID[%s]",uuid);
        }
        try {
            return read(uuid.trim()).getType();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public boolean equals(Object obj) {
        if ((obj != null) && (obj instanceof PKUuid)) {
            return ((this.uuid.equals(((PKUuid) obj).uuid)) && (this.type
                    .equals(((PKUuid) obj).type)));
        }

        return false;
    }

    public int hashCode() {
        return this.uuid.hashCode();
    }

    private void writeObject(ObjectOutputStream s) throws IOException {
        this.uuid.write(s);
        this.type.write(s);
        s.writeByte(this.encoding);
    }

    private void readObject(ObjectInputStream s) throws IOException,
            ClassNotFoundException {
        this.uuid = Uuid.read(s);
        this.type = PKEntityType.read(s);
        this.encoding = s.readByte();
    }

    public void write(DataOutput out) throws IOException {
        this.uuid.write(out);
        this.type.write(out);
        out.writeByte(this.encoding);
    }

    public static PKUuid read(DataInput in) throws IOException {
        return new PKUuid(Uuid.read(in), PKEntityType.read(in), in.readByte());
    }

    public static void main(String[] args) throws Exception {
        for (int i =  0; i < 100000; i ++) {
            PKUuid u = PKUuid.create("F8F02AC9");
            System.out.println(i + "--> " + u.toString());
            String uuid = u.toString();
            //	String uuid2 = "ai4/nxcfc4DgVQAAAAAAAfjwKsk=";

            PKEntityType bosType = PKUuid.read(uuid).getType();

            if (!bosType.toString().equals("F8F02AC9")) {
                System.out.println("false");
            }
            //System.out.println(bosType);

            //	ECAObjectType bosType2 = uuid.read(uuid2).getType();
            //System.out.println(bosType2);

        }

    }
}
